package com.cdn.clock.aspect;

import com.cdn.clock.anno.CRateLimit;
import com.cdn.clock.component.IRateComponent;
import com.cdn.clock.handler.CRateFailure;
import com.cdn.clock.handler.DefaultCRateFailure;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 蔡定努
 * 2024/06/01 23:21
 */
@Aspect
public class CRateAspect {

    @Autowired
    private IRateComponent rateComponent;

    @Autowired
    private ApplicationContext applicationContext;
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    @Around("@annotation(com.cdn.clock.anno.CRateLimit)")
    public Object point(ProceedingJoinPoint point) throws Throwable {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        Object[] args = point.getArgs();
        CRateLimit cRateLimit = method.getAnnotation(CRateLimit.class);
        long tokenCount = cRateLimit.count();
        long rateInterval = cRateLimit.rateInterval();
        TimeUnit timeUnit = cRateLimit.timeUnit();
        if (rateInterval <= 0) {
            throw new IllegalArgumentException("rateInterval must be greater than 0 ");
        }
        String key = getKey(method, cRateLimit, point, args);
        boolean b = rateComponent.rateLimit(key, tokenCount, rateInterval,timeUnit);
        if (b) {
            return point.proceed();
        }
        Map<String, CRateFailure> failureMap = applicationContext.getBeansOfType(CRateFailure.class);
        String tipMsg = cRateLimit.tipMsg();
        if (!failureMap.isEmpty()) {
            for (CRateFailure failure : failureMap.values()) {
                failure.onLockFailure(key, method, args, tipMsg);
            }
        } else {
            DefaultCRateFailure defaultCRateFailure = new DefaultCRateFailure();
            defaultCRateFailure.onLockFailure(key, method, args, tipMsg);
        }
        return null;
    }

    /**
     * @author 蔡定努
     */
    private String getKey(Method method, CRateLimit cRateLimit, ProceedingJoinPoint point, Object[] args) {
        StringBuilder paramValue = new StringBuilder();
        String[] value = cRateLimit.key();
        StandardEvaluationContext context = getEvaluationContext(method, args);
        for (String s : value) {
            if (s.contains("#")) {
                Expression expression = EXPRESSION_PARSER.parseExpression(s);
                Object value1 = expression.getValue(context);
                if (null != value1) {
                    paramValue.append(value1);
                }
            } else {
                paramValue.append(s);
            }
        }
        String methName = method.getName();
        Object target = point.getTarget();
        String className = target.getClass().getName();
        return className + "." + methName + "." + paramValue;
    }

    /**
     * 获取方法上的参数
     *
     * @param method 方法
     * @param args   变量
     * @return {SimpleEvaluationContext}
     */
    // private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
    //     // 初始化Spel表达式上下文
    //     StandardEvaluationContext context = new StandardEvaluationContext();
    //     // 设置表达式支持spring bean
    //     context.setBeanResolver(new BeanFactoryResolver(applicationContext));
    //     Parameter[] parameters = method.getParameters();
    //     for (int i = 0; i < parameters.length; i++) {
    //         context.setVariable(parameters[i].getName(), args[i]);
    //     }
    //     return context;
    // }


    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


    /**
     * 获取方法上的参数
     *
     * @param method 方法
     * @param args   变量
     * @return {SimpleEvaluationContext}
     */
    private MethodBasedEvaluationContext getEvaluationContext(Method method, Object[] args) {
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(null, method, args, parameterNameDiscoverer);
        context.setBeanResolver(new BeanFactoryResolver(applicationContext));
        return context;
    }
}
